#pragma once

namespace AVUI {

class HybridDictionary;

/// <summary>Parses a new URI scheme. This is an abstract class.</summary>
class UriParser : public Object
{
    friend class BuiltInUriParser;
public:
    DECLARE_ELEMENT(UriParser, Object);

#pragma region Public Methods

    /// <summary>Indicates whether the parser for a scheme is registered.</summary>
    /// <returns>true if <paramref name="schemeName" /> has been registered; otherwise, false.</returns>
    /// <param name="schemeName">The scheme name to check.</param>
    /// <exception cref="T:System.ArgumentNullException">The <paramref name="schemeName" /> parameter is null.</exception>
    /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="schemeName" /> parameter is not valid. </exception>
    static bool IsKnownScheme(String* pSchemeName);

    /// <summary>Associates a scheme and port number with a <see cref="T:System.UriParser" />.</summary>
    /// <param name="uriParser">The URI parser to register.</param>
    /// <param name="schemeName">The name of the scheme that is associated with this parser.</param>
    /// <param name="defaultPort">The default port number for the specified scheme.</param>
    /// <exception cref="T:System.ArgumentNullException">The <paramref name="uriParser" /> or <paramref name="schemeName" /> parameter is null.</exception>
    /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="schemeName" /> or <paramref name="defaultPort" /> parameter is not valid. </exception>
    static void Register(UriParser* pUriParser, String* pSchemeName, int defaultPort);

#pragma endregion Public Methods

protected:

#pragma region Protected Methods

    /// <summary>Constructs a default URI parser.</summary>
    UriParser()
    {
        m_Flags = UriSyntaxFlags::MayHavePath;
        m_pScheme = String::get_Empty();
    }

    /// <summary>Gets the components from a URI.</summary>
    /// <returns>A string that contains the components.</returns>
    /// <param name="uri">The URI to parse.</param>
    /// <param name="components">The <see cref="T:System.UriComponents" /> to retrieve from <paramref name="uri" />.</param>
    /// <param name="format">One of the <see cref="T:System.UriFormat" /> values that controls how special characters are escaped.</param>
    /// <exception cref="T:System.ArgumentOutOfRangeException">
    /// <paramref name="uriFormat" /> is invalid.- or -<paramref name="uriComponents" /> is not a combination of valid <see cref="T:System.UriComponents" /> values. </exception>
    /// <exception cref="T:System.InvalidOperationException">
    /// <paramref name="uri" /> is not an absolute URI. Relative URIs cannot be used with this method.</exception>
    virtual TRefCountedPtr<String> GetComponents(Uri* pUri, UriComponents::Enum components, UriFormat::Enum format);

    /// <summary>Initialize the state of the parser and validate the URI.</summary>
    /// <param name="uri">The T:System.Uri to validate.</param>
    /// <param name="parsingError">Validation errors, if any.</param>
    virtual void InitializeAndValidate(Uri* pUri, TRefCountedPtr<UriFormatException>* ppParsingError);

    /// <summary>Determines whether <paramref name="baseUri" /> is a base URI for <paramref name="relativeUri" />.</summary>
    /// <returns>true if <paramref name="baseUri" /> is a base URI for <paramref name="relativeUri" />; otherwise, false.</returns>
    /// <param name="baseUri">The base URI.</param>
    /// <param name="relativeUri">The URI to test.</param>
    virtual bool IsBaseOf(Uri* pBaseUri, Uri* pRelativeUri);

 
    /// <summary>Indicates whether a URI is well-formed.</summary>
    /// <returns>true if <paramref name="uri" /> is well-formed; otherwise, false.</returns>
    /// <param name="uri">The URI to check.</param>
    virtual bool IsWellFormedOriginalString(Uri* pUri);

    /// <summary>Invoked by a <see cref="T:System.Uri" /> constructor to get a <see cref="T:System.UriParser" /> instance</summary>
    /// <returns>A <see cref="T:System.UriParser" /> for the constructed <see cref="T:System.Uri" />.</returns>
    virtual TRefCountedPtr<UriParser> OnNewUri() { return this; }
 
    /// <summary>Invoked by the Framework when a <see cref="T:System.UriParser" /> method is registered.</summary>
    /// <param name="schemeName">The scheme that is associated with this <see cref="T:System.UriParser" />.</param>
    /// <param name="defaultPort">The port number of the scheme.</param>
    virtual void OnRegister(String* pSchemeName, int defaultPort) { };

    /// <summary>Called by <see cref="T:System.Uri" /> constructors and <see cref="Overload:System.Uri.TryCreate" /> to resolve a relative URI.</summary>
    /// <returns>The string of the resolved relative <see cref="T:System.Uri" />.</returns>
    /// <param name="baseUri">A base URI.</param>
    /// <param name="relativeUri">A relative URI.</param>
    /// <param name="parsingError">Errors during the resolve process, if any.</param>
    /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="baseUri" /> parameter is not an absolute <see cref="T:System.Uri" />.or the <paramref name="baseUri" /> requires user-driven parsing.</exception>
    virtual TRefCountedPtr<String> Resolve(Uri* pBaseUri, Uri* pRelativeUri, TRefCountedPtr<UriFormatException>* ppParsingError);
 
#pragma endregion Protected Methods


public:

#pragma region Internal Methods

    UriParser(UriSyntaxFlags::Enum flags)
    {
        m_Flags = flags;
        m_pScheme = String::get_Empty();
    }

    void CheckSetIsSimpleFlag();
    static TRefCountedPtr<UriParser> FindOrFetchAsUnknownV1Syntax(String* pLwrCaseScheme);
    static TRefCountedPtr<UriParser> GetSyntax(String* pLwrCaseScheme);
    bool InFact(unsigned int flags) { return (m_Flags & flags) != 0; }
    TRefCountedPtr<String> InternalGetComponents(Uri* pThisUri, UriComponents::Enum uriComponents, UriFormat::Enum uriFormat) { return GetComponents(pThisUri, uriComponents, uriFormat); }
    bool InternalIsBaseOf(Uri* pThisBaseUri, Uri* pUriLink) { return IsBaseOf(pThisBaseUri, pUriLink); }
    bool InternalIsWellFormedOriginalString(Uri* pThisUri) { return IsWellFormedOriginalString(pThisUri); }
    TRefCountedPtr<UriParser> InternalOnNewUri();
    TRefCountedPtr<String> InternalResolve(Uri* pThisBaseUri, Uri* pUriLink, TRefCountedPtr<UriFormatException>* ppParsingError) { return Resolve(pThisBaseUri, pUriLink, ppParsingError); }
    void InternalValidate(Uri* pThisUri, TRefCountedPtr<UriFormatException>* ppParsingError) { InitializeAndValidate(pThisUri, ppParsingError); }
    bool IsAllSet(unsigned int flags) { return (m_Flags & flags) == flags; }
    bool NotAny(unsigned int flags) { return (m_Flags & flags) == 0; }

#pragma endregion Internal Methods

#pragma region Internal Properties

    int get_DefaultPort() { return m_Port; }
    UriSyntaxFlags::Enum get_Flags() { return (UriSyntaxFlags::Enum) m_Flags; }
    bool get_IsSimple() { return InFact(UriSyntaxFlags::SimpleUserSyntax); }
    TRefCountedPtr<String> get_SchemeName() { return m_pScheme; }

    // Known uri handlers
    static UriParser* get_FileUri();
    static UriParser* get_HttpUri();

#pragma endregion Internal Properties

private:

#pragma region Private Methods

    static void StaticConstructor();

    static bool CheckSchemeName(String* pSchemeName);
    static void FetchSyntax(UriParser* pSyntax, String* pLwrCaseSchemeName, int defaultPort);
    static bool IsAsciiLetter(WCHAR character) { return (((character >= 'a') && (character <= 'z')) || ((character >= 'A') && (character <= 'Z'))); }
    static bool IsAsciiLetterOrDigit(WCHAR character) { return (IsAsciiLetter(character) || ((character >= '0') && (character <= '9'))); }

#pragma endregion Private Methods

#pragma region Private Fields

    static TRefCountedPtr<HybridDictionary> m_pTable;
    unsigned int m_Flags;
    int m_Port;
    TRefCountedPtr<String> m_pScheme;

#pragma endregion Private Fields

};

#pragma region BuiltInUriParser

class BuiltInUriParser : public UriParser
{
public:
    DECLARE_ELEMENT(BuiltInUriParser, Object);

    BuiltInUriParser(TRefCountedPtr<String> pLwrCaseScheme, int defaultPort, UriSyntaxFlags::Enum syntaxFlags) : UriParser((UriSyntaxFlags::Enum)(syntaxFlags | UriSyntaxFlags::SimpleUserSyntax | UriSyntaxFlags::BuiltInSyntax))
    {
        m_pScheme = pLwrCaseScheme;
        m_Port = defaultPort;
    }
};
 
#pragma endregion BuiltInUriParser

/// <summary>A customizable parser based on the HTTP scheme.</summary>
class HttpStyleUriParser : public UriParser
{
public:
    DECLARE_ELEMENT(HttpStyleUriParser, UriParser);
    /// <summary>Create a customizable parser based on the HTTP scheme.</summary>
    HttpStyleUriParser() : UriParser(UriParser::get_HttpUri()->get_Flags()) { }
};

/// <summary>A customizable parser based on the File scheme.</summary>
class FileStyleUriParser : public UriParser
{
public:
    DECLARE_ELEMENT(FileStyleUriParser, UriParser);
    /// <summary>Creates a customizable parser based on the File scheme.</summary>
    FileStyleUriParser() : UriParser(UriParser::get_FileUri()->get_Flags()) { }
};


/// <summary>Specifies options for a <see cref="T:System.UriParser" />.</summary>
class GenericUriParserOptions
{
public:
    enum Enum
    {
        /// <summary>The parser allows a URI with no authority.</summary>
        AllowEmptyAuthority = 2,
        /// <summary>The parser:</summary>
        Default = 0,
        /// <summary>The parser does not canonicalize the URI.</summary>
        DontCompressPath = 0x80,
        /// <summary>The parser does not convert back slashes into forward slashes.</summary>
        DontConvertPathBackslashes = 0x40,
        /// <summary>The parser does not unescape path dots, forward slashes, or back slashes.</summary>
        DontUnescapePathDotsAndSlashes = 0x100,
        /// <summary>The parser allows a registry-based authority.</summary>
        GenericAuthority = 1,
        /// <summary>The parser supports Internationalized Domain Name (IDN) parsing (IDN) of host names. Whether IDN is used is dictated by configuration values. See the Remarks for more information.</summary>
        Idn = 0x200,
        /// <summary>The parser supports the parsing rules specified in RFC 3987 for International Resource Identifiers (IRI). Whether IRI is used is dictated by configuration values. See the Remarks for more information.</summary>
        IriParsing = 0x400,
        /// <summary>The scheme does not define a fragment part.</summary>
        NoFragment = 0x20,
        /// <summary>The scheme does not define a port.</summary>
        NoPort = 8,
        /// <summary>The scheme does not define a query part.</summary>
        NoQuery = 0x10,
        /// <summary>The scheme does not define a user information part.</summary>
        NoUserInfo = 4
    };
};

/// <summary>A customizable parser for a hierarchical URI.</summary>
class GenericUriParser : public UriParser
{
public:
    DECLARE_ELEMENT(GenericUriParser, UriParser);

    /// <summary>Create a customizable parser for a hierarchical URI.</summary>
    /// <param name="options">Specify the options for this <see cref="T:System.GenericUriParser" />.</param>
    GenericUriParser(GenericUriParserOptions::Enum options) : UriParser(MapGenericParserOptions(options)) { }

private:
    static UriSyntaxFlags::Enum MapGenericParserOptions(GenericUriParserOptions::Enum options);
};
 


};


